#include "stdlib.hpp"
#include <purewell/types.hpp>
#include <purewell/thread.hpp>

namespace purewell
{

typedef struct _pwthread_t
{
	pthread_t		thread_id;
	void*			thread_args;
	void*			thread_return;

	_pwthread_t()
		: thread_args(NULL), thread_return(NULL)
	{}

} pwthread_t;

PWThreadAttribute::PWThreadAttribute()
{
	pthread_attr_t* pthis(new pthread_attr_t);
	if ( !pthis ) return;

	if ( 0 != pthread_attr_init(pthis) )
	{
		delete pthis;
		return;
	}

	m_pData = pthis;
	return;
}

PWThreadAttribute::~PWThreadAttribute()
{
	if ( m_pData )
	{
		pthread_attr_t* pthis((pthread_attr_t*)m_pData);
		pthread_attr_destroy(pthis);
		delete pthis;
		m_pData = NULL;
	}
}

PWThreadAttribute*
PWThreadAttribute::create(void)
{
	PWThreadAttribute* pattr(new PWThreadAttribute);
	if ( !pattr ) return NULL;
	if ( !pattr->m_pData )
	{
		delete pattr;
		return NULL;
	}
	return pattr;
}

PWThreadAttribute*
PWThreadAttribute::createArray(size_t count)
{
	PWThreadAttribute* pattr(new PWThreadAttribute[count]);
	if ( !pattr ) return NULL;

	size_t i(0);
	while ( i < count )
	{
		if ( !pattr[i].m_pData )
		{
			delete [] pattr;
			return NULL;
		}

		++i;
	}

	return pattr;
}

void
PWThreadAttribute::release(PWThreadAttribute* op)
{
	delete op;
}

void
PWThreadAttribute::releaseArray(PWThreadAttribute* op)
{
	delete [] op;
}

bool
PWThreadAttribute::setScope(int type)
{
	return 0 == pthread_attr_setscope((pthread_attr_t*)m_pData, type);
}

bool
PWThreadAttribute::getScope(int& type) const
{
	if ( 0 != pthread_attr_getscope((pthread_attr_t*)m_pData, &type) )
	{
		return false;
	}

	return true;
}

bool
PWThreadAttribute::setDetach(bool detached)
{
	const int detype(detached
		?PTHREAD_CREATE_DETACHED:PTHREAD_CREATE_JOINABLE);
	return 0 == pthread_attr_setdetachstate((pthread_attr_t*)m_pData,
		detype);
}

bool
PWThreadAttribute::getDetach(bool& detached) const
{
	int detype;

	if ( 0 != pthread_attr_getdetachstate((pthread_attr_t*)m_pData,
		&detype) )
	{
		return false;
	}

	detached = ( detype == PTHREAD_CREATE_DETACHED );
	return true;
}

bool
PWThreadAttribute::setGuardSize(size_t size)
{
	return 0 == pthread_attr_setguardsize((pthread_attr_t*)m_pData, size);
}

bool
PWThreadAttribute::getGuardSize(size_t& size) const
{
	return 0 == pthread_attr_getguardsize((pthread_attr_t*)m_pData, &size);
}

bool
PWThreadAttribute::setStack(void* stack, size_t size)
{
	return 0 == pthread_attr_setstack((pthread_attr_t*)m_pData,
		stack, size);
}

bool
PWThreadAttribute::getStack(void*& stack, size_t& size) const
{
	return 0 == pthread_attr_getstack((pthread_attr_t*)m_pData,
		&stack, &size);
}

bool
PWThreadAttribute::setStackSize(size_t size)
{
	return 0 == pthread_attr_setstacksize((pthread_attr_t*)m_pData,
		size);
}

bool
PWThreadAttribute::getStackSize(size_t& size) const
{
	return 0 == pthread_attr_getstacksize((pthread_attr_t*)m_pData,
		&size);
}

void*
PWThread::_threadProc(void* param)
{
	PWThread* pthr((PWThread*)param);
	pwthread_t* pthis((pwthread_t*)pthr->m_pData);
	void* ret(pthr->onStart(pthis->thread_args));
	pthr->exit(ret);
	return ret;
}

PWThread::PWThread()
{
}

PWThread::PWThread(const PWThread&)
{
	::abort();
}

PWThread::~PWThread()
{
}

bool
PWThread::initialize(void* param, const PWThreadAttribute* pattr)
{
	pwthread_t* pthis(new pwthread_t);
	if ( NULL == pthis )
	{
		_PWTRACE("%s", "not enough memory");
		return false;
	}

	pthis->thread_args = param;
	pthread_attr_t* pthrattr(pattr?(pthread_attr_t*)pattr->getData():NULL);

	int res( pthread_create(&pthis->thread_id,
		pthrattr,
		this->_threadProc, this) );

	if ( 0 != res )
	{
		_PWTRACE("%s", strerror(res));
		delete pthis;
		pthis = NULL;
		m_pData = NULL;
		return false;
	}

	m_pData = pthis;

	return true;
}

void
PWThread::destroy(void)
{
	if ( m_pData )
	{
		pwthread_t* pthis((pwthread_t*)m_pData);
		delete pthis;
		m_pData = NULL;
	}
}

void
PWThread::detach(void)
{
	pwthread_t* pthis((pwthread_t*)m_pData);
	pthread_detach(pthis->thread_id);
}

bool
PWThread::wait(void** ret)
{
	pwthread_t& data(*(pwthread_t*)m_pData);
	int res(pthread_join(data.thread_id, &data.thread_return) );
	if ( 0 != res )
	{
		_PWTRACE("%s", strerror(res));
		if ( ret )
		{
			*ret = NULL;
		}

		return false;
	}

	if ( ret )
	{
		*ret = data.thread_return;
	}

	return true;
}

void
PWThread::exit(void* value)
{
	pthread_exit(value);
}

void*
PWThread::onStart(void* value)
{
	_PWTRACE("input: %p", value);
	sleep(5);
	this->exit(this);
	return this;
}

void*
PWThread::getResult(void)
{
	return ((pwthread_t*)m_pData)->thread_return;
}

const void*
PWThread::getResult(void) const
{
	return ((pwthread_t*)m_pData)->thread_return;
}

void*
PWThread::getParameter(void)
{
	return ((pwthread_t*)m_pData)->thread_args;
}

const void*
PWThread::getParameter(void) const
{
	return ((pwthread_t*)m_pData)->thread_args;
}

};//namespace purewell

